package com.szeldon.declatest.exec;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import com.szeldon.declatest.exec.events.CheckpointExecEvent;
import com.szeldon.declatest.exec.events.ElseConditionEvaluationEndExecEvent;
import com.szeldon.declatest.exec.events.ElseConditionEvaluationStartExecEvent;
import com.szeldon.declatest.exec.events.IfConditionEvaluationEndExecEvent;
import com.szeldon.declatest.exec.events.IfConditionEvaluationStartExecEvent;
import com.szeldon.declatest.exec.events.ScenarioExecutionEndExecEvent;
import com.szeldon.declatest.exec.events.ScenarioExecutionStartExecEvent;
import com.szeldon.declatest.exec.events.TestSuiteExecEvent;
import com.szeldon.declatest.exec.events.dostep.DoScenarioStepEndExecEvent;
import com.szeldon.declatest.exec.events.dostep.DoScenarioStepStartExecEvent;
import com.szeldon.declatest.exec.events.dostep.jms.JmsMessageReceivedExecEvent;
import com.szeldon.declatest.exec.events.dostep.jms.JmsMessageSentExecEvent;
import com.szeldon.declatest.exec.result.ScenarioExecutionResult;

/**
 * Listener of {@link AbstractTestsSuiteExecutor} events that has compares listener's events with events added
 * previously using {@link #addEventToCompare(TestSuiteExecEvent)} method. Usage of this class is based on this scheme:
 * <ul>
 * <li>Add events ({@link #addEventToCompare(TestSuiteExecEvent)}) one by one in order in which they're expected to be
 * performed by executor</li>
 * <li>Add this object to executor as listener</li>
 * <li>After the execution of executor ends, {@link #isExecutionOk()} method returns true, if
 * {@link #actionPerformed(TestSuiteExecEvent)} method was invoked by executor with events in order in which they were
 * added to this object. More information about why execution wasn't OK is provided by
 * {@link #notOkExecutionDescription()} method.</li>
 * </ul>
 * This class has to be modified each time new event is added to executor.
 * 
 * @author szeldon
 * 
 */
public class ExecListenerTester implements ITestSuiteExecutorListener {

    private static Logger logger = Logger.getLogger(ExecListenerTester.class.getName());

    private List<TestSuiteExecEvent> eventsList = new ArrayList<TestSuiteExecEvent>();

    private int currentEventsIndex = 0;

    private String errorDescription = null;

    @Override
    public void actionPerformed(TestSuiteExecEvent event) {

        logger.finer("event received: " + event.toString());

        if (currentEventsIndex < eventsList.size()) {
            TestSuiteExecEvent expectedEvent = eventsList.get(currentEventsIndex);
            // if names of classes for expected and given event match
            if (event.getClass().getName().equals(expectedEvent.getClass().getName())) {
                if (event instanceof ScenarioExecutionStartExecEvent) {
                    analyzeScenarioExecutionStart((ScenarioExecutionStartExecEvent) event,
                            (ScenarioExecutionStartExecEvent) expectedEvent);
                } else if (event instanceof IfConditionEvaluationStartExecEvent) {
                    analyzeIfConditionStart((IfConditionEvaluationStartExecEvent) event,
                            (IfConditionEvaluationStartExecEvent) expectedEvent);
                } else if (event instanceof IfConditionEvaluationEndExecEvent) {
                    analyzeIfConditionEnd((IfConditionEvaluationEndExecEvent) event,
                            (IfConditionEvaluationEndExecEvent) expectedEvent);
                } else if (event instanceof ElseConditionEvaluationStartExecEvent) {
                    analyzeElseConditionStart((ElseConditionEvaluationStartExecEvent) event,
                            (ElseConditionEvaluationStartExecEvent) expectedEvent);
                } else if (event instanceof ElseConditionEvaluationEndExecEvent) {
                    analyzeElseConditionEnd((ElseConditionEvaluationEndExecEvent) event,
                            (ElseConditionEvaluationEndExecEvent) expectedEvent);
                } else if (event instanceof CheckpointExecEvent) {
                    analyzeCheckpointExec((CheckpointExecEvent) event, (CheckpointExecEvent) expectedEvent);
                } else if (event instanceof ScenarioExecutionEndExecEvent) {
                    analyzeScenarioExecutionEnd((ScenarioExecutionEndExecEvent) event,
                            (ScenarioExecutionEndExecEvent) expectedEvent);
                } else if (event instanceof DoScenarioStepStartExecEvent) {
                    analyzeDoScenarioStepStart((DoScenarioStepStartExecEvent) event,
                            (DoScenarioStepStartExecEvent) expectedEvent);
                } else if (event instanceof DoScenarioStepEndExecEvent) {
                    analyzeDoScenarioStepEnd((DoScenarioStepEndExecEvent) event,
                            (DoScenarioStepEndExecEvent) expectedEvent);
                } else if (event instanceof JmsMessageSentExecEvent) {
                    analyzeJmsMessageSent((JmsMessageSentExecEvent) event, (JmsMessageSentExecEvent) expectedEvent);
                } else if (event instanceof JmsMessageReceivedExecEvent) {
                    analyzeJmsMessageReceived((JmsMessageReceivedExecEvent) event,
                            (JmsMessageReceivedExecEvent) expectedEvent);
                } else {
                    errorDescription = event.getClass().getSimpleName() + " isn't a supported event for tests.# ";
                }
            } else {
                makeErrorDescription(event, expectedEvent);
            }
        } else {
            if (errorDescription == null) {
                errorDescription = toNth(currentEventsIndex) + " event of " + event.getClass().getName()
                        + " type wasn't suppose to happen.# ";
            } else {
                errorDescription += toNth(currentEventsIndex) + " event of " + event.getClass().getName()
                        + " type wasn't suppose to happen.# ";
            }
        }

        currentEventsIndex++;
    }

    /**
     * Adds given event. It will be used for comparison with events got from executor. Order in which events are added,
     * is the order which this class expects to get from executor.
     * 
     * @param event
     */
    public void addEventToCompare(TestSuiteExecEvent event) {
        eventsList.add(event);
    }

    /**
     * 
     * @return
     */
    public boolean isExecutionOk() {
        if (errorDescription == null && eventsList.size() == currentEventsIndex) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns description of error in case when error happened. Null is returned if all events were OK.
     * 
     * @return
     */
    public String notOkExecutionDescription() {
        if (errorDescription != null) {
            return errorDescription;
        } else if (eventsList.size() != currentEventsIndex) {
            return "Expected " + eventsList.size() + " events. Got " + currentEventsIndex + ".";
        } else {
            return null;
        }

    }

    /**
     * Adds error description for given events.
     * 
     * @param event
     * @param expectedEvent
     * 
     */
    private void makeErrorDescription(TestSuiteExecEvent event, TestSuiteExecEvent expectedEvent) {
        String toBeAdded = "Execution step=" + toNth(currentEventsIndex) + "; expected event "
                + expectedEvent.getClass().getSimpleName() + "[" + expectedEvent + "]; got event "
                + event.getClass().getSimpleName() + "[" + event + "]# ";
        if (errorDescription == null) {
            errorDescription = toBeAdded;
        } else {
            errorDescription += toBeAdded;
        }
    }

    /**
     * Returns given number with following "-th", "-nd", "-rd" or "-st" depending on the value provided.
     * 
     * @param number
     * @return
     */
    private static String toNth(int number) {
        String result = null;
        switch (number) {
        case 1:
            result = "1-st";
            break;
        case 2:
            result = "2-nd";
            break;
        case 3:
            result = "3-rd";
            break;
        default:
            result = number + "-th";
        }
        return result;
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeScenarioExecutionStart(ScenarioExecutionStartExecEvent event,
            ScenarioExecutionStartExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeIfConditionStart(IfConditionEvaluationStartExecEvent event,
            IfConditionEvaluationStartExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getLeft().equals(expectedEvent.getLeft())
                || !event.getOperator().equals(expectedEvent.getOperator())
                || !event.getRight().equals(expectedEvent.getRight())
                || !event.getIfDescription().equals(expectedEvent.getIfDescription())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeIfConditionEnd(IfConditionEvaluationEndExecEvent event,
            IfConditionEvaluationEndExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getEvaluatedLeft().equals(expectedEvent.getEvaluatedLeft())
                || !event.getOperator().equals(expectedEvent.getOperator())
                || !event.getEvaluatedRight().equals(expectedEvent.getEvaluatedRight())
                || !event.getIfDescription().equals(expectedEvent.getIfDescription())
                || !(event.getEvaluationResult() == expectedEvent.getEvaluationResult())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeElseConditionStart(ElseConditionEvaluationStartExecEvent event,
            ElseConditionEvaluationStartExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getLeft().equals(expectedEvent.getLeft())
                || !event.getOperator().equals(expectedEvent.getOperator())
                || !event.getRight().equals(expectedEvent.getRight())
                || !event.getIfDescription().equals(expectedEvent.getIfDescription())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeElseConditionEnd(ElseConditionEvaluationEndExecEvent event,
            ElseConditionEvaluationEndExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getEvaluatedLeft().equals(expectedEvent.getEvaluatedLeft())
                || !event.getOperator().equals(expectedEvent.getOperator())
                || !event.getEvaluatedRight().equals(expectedEvent.getEvaluatedRight())
                || !event.getIfDescription().equals(expectedEvent.getIfDescription())
                || !(event.getEvaluationResult() == expectedEvent.getEvaluationResult())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeCheckpointExec(CheckpointExecEvent event, CheckpointExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getCheckpointId().equals(expectedEvent.getCheckpointId())
                || !event.getCheckpointDescription().equals(expectedEvent.getCheckpointDescription())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeScenarioExecutionEnd(ScenarioExecutionEndExecEvent event,
            ScenarioExecutionEndExecEvent expectedEvent) {
        ScenarioExecutionResult result = event.getResult();
        ScenarioExecutionResult expectedResult = expectedEvent.getResult();
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !result.getClass().getName().equals(expectedResult.getClass().getName())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeDoScenarioStepStart(DoScenarioStepStartExecEvent event,
            DoScenarioStepStartExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getStepId().equals(expectedEvent.getStepId())
                || !event.getStepDescription().equals(expectedEvent.getStepDescription())
                || !event.getStepEndpointId().equals(expectedEvent.getStepEndpointId())
                || !event.getStepRequestPath().equals(expectedEvent.getStepRequestPath())
                || !event.getEndpointDescription().equals(expectedEvent.getEndpointDescription())
                || !event.getEndpointUrl().equals(expectedEvent.getEndpointUrl())
                || !event.getEndpointSettingsId().equals(expectedEvent.getEndpointSettingsId())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeDoScenarioStepEnd(DoScenarioStepEndExecEvent event, DoScenarioStepEndExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getStepId().equals(expectedEvent.getStepId())
                || !event.getStepDescription().equals(expectedEvent.getStepDescription())
                || !event.getStepEndpointId().equals(expectedEvent.getStepEndpointId())
                || !event.getStepRequestPath().equals(expectedEvent.getStepRequestPath())
                || !event.getEndpointDescription().equals(expectedEvent.getEndpointDescription())
                || !event.getEndpointUrl().equals(expectedEvent.getEndpointUrl())
                || !event.getEndpointSettingsId().equals(expectedEvent.getEndpointSettingsId())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeJmsMessageSent(JmsMessageSentExecEvent event, JmsMessageSentExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getStepId().equals(expectedEvent.getStepId())
                || !event.getStepDescription().equals(expectedEvent.getStepDescription())
                || !event.getStepEndpointId().equals(expectedEvent.getStepEndpointId())
                || !event.getStepRequestPath().equals(expectedEvent.getStepRequestPath())
                || !event.getEndpointDescription().equals(expectedEvent.getEndpointDescription())
                || !event.getEndpointUrl().equals(expectedEvent.getEndpointUrl())
                || !event.getEndpointSettingsId().equals(expectedEvent.getEndpointSettingsId())
                || !event.getDestinationType().equals(expectedEvent.getDestinationType())
                || !event.getDestinationName().equals(expectedEvent.getDestinationName())
                || !event.getMessageContent().equals(expectedEvent.getMessageContent())) {
            makeErrorDescription(event, expectedEvent);
        }
    }

    /**
     * 
     * @param event
     * @param expectedEvent
     */
    private void analyzeJmsMessageReceived(JmsMessageReceivedExecEvent event, JmsMessageReceivedExecEvent expectedEvent) {
        if (!event.getScenarioId().equals(expectedEvent.getScenarioId())
                || !event.getStepId().equals(expectedEvent.getStepId())
                || !event.getStepDescription().equals(expectedEvent.getStepDescription())
                || !event.getStepEndpointId().equals(expectedEvent.getStepEndpointId())
                || !event.getStepRequestPath().equals(expectedEvent.getStepRequestPath())
                || !event.getEndpointDescription().equals(expectedEvent.getEndpointDescription())
                || !event.getEndpointUrl().equals(expectedEvent.getEndpointUrl())
                || !event.getEndpointSettingsId().equals(expectedEvent.getEndpointSettingsId())
                || !event.getDestinationType().equals(expectedEvent.getDestinationType())
                || !event.getDestinationName().equals(expectedEvent.getDestinationName())
                || !event.getMessageContent().equals(expectedEvent.getMessageContent())) {
            makeErrorDescription(event, expectedEvent);
        }
    }
}
